package scau.mis4.chanyulin;

import scau.mis4.chanyulin.utils.BitsUtils;
import scau.mis4.chanyulin.utils.ByteUtils;
import scau.mis4.chanyulin.utils.CipUtils;
import scau.mis4.chanyulin.utils.HexUtils;
import java.util.Arrays;

/**
 * Created by cyl on 2017/12/20.
 * 64位的DES加密实现（书本P53例子）
 */
public class DES {

    private static final String P_STR = "02468aceeca86420";    // 明文
    private static final String K_STR = "0f1571c947d9e859";    // 密钥

    public static void main(String[] args) {
        // 将明文和密钥转换为十六进制字节
        byte[] pBytes = HexUtils.toBytes(P_STR);
        byte[] kBytes = HexUtils.toBytes(K_STR);
        // 初始状态
        printHexStrResult("【初始化】明文：", pBytes);
        printHexStrResult("【初始化】密钥：", kBytes);
        // 生成16个子密钥（48位的位数组）
        byte[][] kList = makeKList(pBytes);
        for (int i = 0; i < kList.length; i++) printHexStrResult("\n【第" + ( i + 1 ) + "轮】子密钥：", bits2Bytes(kList[i]));
        // 明文处理
        byte[] pBits = BitsUtils.fromBytes(pBytes);
        // 分割为左右两部分
        byte[] pL = getL(pBits);
        byte[] pR = getR(pBits);
        // 16轮的加密迭代
        for (int i = 1; i <= 16; i++) {
            printHexStrResult("\n【第" + i + "轮】L：", bits2Bytes(pL));
            printHexStrResult("\n【第" + i + "轮】R：", bits2Bytes(pR));
            byte[] copyR = pR;
            byte[] expandRes = pExpandAndShuff(copyR);  // 明文的扩展和置换
            byte[] xorRes = BitsUtils.xor(expandRes, kList[i - 1]);     // 和子密钥求异或
            byte[] sBoxRes = sBox(xorRes);  // S盒
            byte[] rShuffRes = rShuff(sBoxRes);
            byte[] rlXorRes = BitsUtils.xor(pL, rShuffRes);
            pL = copyR;
            pR = rlXorRes;
        }
        // 最后一次置换
        byte[] finalRes = finalShuff(ByteUtils.appendBytes(pL, pR));
        // 最终结果
        byte[] finalBytes = bits2Bytes(finalRes);
        printHexStrResult("\n【结果】密文：", finalBytes);
    }

    /**
     * 最后一次置换
     * @param input
     * @return
     */
    public static byte[] finalShuff(byte[] input) {
        int[] posArr = new int[] {
            40, 8, 48, 16, 56, 24, 64, 32, 39, 7, 47, 15, 55, 23, 63, 31,
            38, 6, 46, 14, 54, 22, 62, 30, 37, 5, 45, 13, 53, 21, 61, 29,
            36, 4, 44, 12, 52, 20, 60, 28, 35, 3, 43, 11, 51, 19, 59, 27,
            34, 2, 42, 10, 50, 18, 58, 26, 33, 1, 41, 9, 49, 17, 57, 25
        };
        return shuffle(posArr, input);
    }

    /**
     * 对明文经过S盒出力后部分进行置换
     * @param input
     * @return
     */
    public static byte[] rShuff(byte[] input) {
        int[] posArr = new int[] {
                16, 7, 20, 21, 29, 12, 28, 17, 1, 15, 23, 26, 5, 18, 31, 10,
                2, 8, 24, 14, 32, 27, 3, 9, 19, 13, 30, 6, 22, 11, 4, 25
        };
        return shuffle(posArr, input);
    }

    /**
     * 从明文获取置换后的左半部分
     * @param pBits
     * @return
     */
    public static byte[] getL(byte[] pBits) {
        int[] posArr = new int[] {
                58, 50, 42, 34, 26, 18, 10, 2, 60, 52, 44, 36, 28, 20, 12, 4,
                62, 54, 46, 38, 30, 22, 14, 6, 64, 56, 48, 40, 32, 24, 16, 8
        };
        return shuffle(posArr, pBits);
    }

    /**
     * 从明文获取置换后的右半部分
     * @param pBits
     * @return
     */
    public static byte[] getR(byte[] pBits) {
        int[] posArr = new int[] {
                57, 49, 41, 33, 25, 17, 9, 1, 59, 51, 43, 35, 27, 19, 11, 3,
                61, 53, 45, 37, 29, 21, 13, 5, 63, 55, 47, 39, 31, 23, 15, 7
        };
        return shuffle(posArr, pBits);
    }

    /**
     * S盒代换
     * @param input
     * @return
     */
    public static byte[] sBox(byte[] input) {
        byte[][] DES_SBOX = CipUtils.DES_SBOX;
        byte[] result = new byte[32];
        for (int i = 0; i < 8; i++) {
            byte[] temp = Arrays.copyOfRange(input, 6 * i, 6 * ( i + 1 ));
            int row = BitsUtils.toDec(new byte[] {temp[0], temp[5]});
            int col = BitsUtils.toDec(new byte[] {temp[1], temp[2], temp[3], temp[4]});
            byte[] resultBits = BitsUtils.fromDec(DES_SBOX[i * 4 + row][col], 4);
            for (int j = 0; j < resultBits.length; j++) result[4 * i + j] = resultBits[j];
        }
        return result;
    }

    /**
     * 32位明文的扩展和置换
     * @param input
     * @return
     */
    public static byte[] pExpandAndShuff(byte[] input) {
        int[] posArr = new int[] {
                32, 1, 2, 3, 4, 5, 4, 5, 6, 7, 8, 9, 8, 9, 10, 11,
                12, 13, 12, 13, 14, 15, 16, 17, 16, 17, 18, 19, 20, 21, 20, 21,
                22, 23, 24, 25, 24, 25, 26, 27, 28, 29, 28, 29, 30, 31, 32, 1
        };
        return shuffle(posArr, input);
    }

    /**
     * 生成16个子密钥,返回为位数组
     * @param p
     * @return
     */
    public static byte[][] makeKList(byte[] p) {
        byte[][] temp = new byte[16][56];
        byte[][] result = new byte[16][48];
        byte[][] twoGuys = k1stShuff(p);    // 置换
        byte[] c = twoGuys[0];
        byte[] d = twoGuys[1];
        for (int i = 1; i <= temp.length; i++) {
            c = kMovLeft(c, i);
            d = kMovLeft(d, i);
            temp[i - 1] = ByteUtils.appendBytes(c, d);
        }
        for (int j = 0; j < result.length; j++) result[j] = k2ndShuff(temp[j]); // 压缩置换
        return result;
    }

    /**
     * 密钥第一次压缩和置换,返回为位数组
     * @param p
     * @return
     */
    public static byte[][] k1stShuff(byte[] p) {
        byte[] pBits = BitsUtils.fromBytes(p);
        byte[][] result = new byte[2][28];
        int[] cPosArr = {57, 49, 41, 33, 25, 17, 9, 1, 58, 50, 42, 34, 26, 18, 10, 2, 59, 51, 43, 35, 27, 19, 11, 3, 60, 52, 44, 36};
        int[] dPosArr = {63, 55, 47, 39, 31, 23, 15, 7, 62, 54, 46, 38, 30, 22, 14, 6, 61, 53, 45, 37, 29, 21, 13, 5, 28, 20, 12, 4};
        result[0] = shuffle(cPosArr, pBits);
        result[1] = shuffle(dPosArr, pBits);
        return result;
    }

    /**
     * 将位数组转为字节数组
     * @param bits
     * @return
     */
    public static byte[] bits2Bytes(byte[] bits) {
        if (bits.length % 8 != 0) return null;
        byte[] result = new byte[bits.length / 8];
        for (int i = 0; i < result.length; i++) {
            result[i] = BitsUtils.toByte(Arrays.copyOfRange(bits, i * 8, (i + 1) * 8));
        }
        return result;
    }

    /**
     * 密钥第二次压缩和置换
     * @param input
     * @return
     */
    public static byte[] k2ndShuff(byte[] input) {
        int[] posArr = {14, 17, 11, 24, 1, 5, 3, 28, 15, 6, 21, 10, 23, 19, 12, 4, 26, 8, 16, 7, 27, 20, 13, 2, 41, 52, 31, 37, 47, 55, 30, 40, 51, 45, 33, 48, 44, 49, 39, 56, 34, 53, 46, 42, 50, 36, 29, 32};
        return shuffle(posArr, input);
    }
    
    /**
     * 密钥进行左移位
     * @param input
     * @param round
     * @return
     */
    public static byte[] kMovLeft(byte[] input, int round) {
        int[] movCountArr = {1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1};
        return ByteUtils.movLeft(input, movCountArr[round - 1]);
    }

    /**
     * 位置置换通用方法
     * @param posArr
     * @param input
     * @return
     */
    public static byte[] shuffle(int[] posArr, byte[] input) {
        byte[] output = new byte[posArr.length];
        for (int i = 0; i < posArr.length; i++) output[i] = input[posArr[i] - 1];
        return output;
    }

    /**
     * 结果以十六进制字符串格式输出
     * @param des
     * @param bytes
     */
    public static void printHexStrResult(String des, byte[] bytes) {
        System.out.println(des);
        String[] strArr = new String[bytes.length];
        for (int i = 0; i < strArr.length; i++) strArr[i] = HexUtils.fromByte(bytes[i]);
        int temp = 1;
        for (String str: strArr) {
            if (temp <= 3)
                System.out.print(str + " ");
            else {
                System.out.print(str + "\n");
                temp = 0;
            }
            temp++;
        }
    }

}
